package com.leyou.search.service;

import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecificationClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.pojo.SearchRequest;
import com.leyou.search.pojo.SearchResult;
import com.leyou.search.repository.GoodsRepository;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchService {

    @Autowired
    private CategoryClient categoryClient;

    @Autowired
    private SpecificationClient specificationClient;

    @Autowired
    private BrandClient brandClient;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private GoodsClient goodsClient;

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ImportService importService;

    private static final Logger logger = LoggerFactory.getLogger(SearchService.class);

    //搜索方法
    public SearchResult search(SearchRequest request) {
        // 判断是否有搜索条件，如果没有，直接返回null,不允许搜索全部商品
        if (StringUtils.isBlank(request.getKey())) {
            //返回默认值
            return null;
        }

        // 1. 构建自定义搜索构建器
        NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();

        // 1.1 添加基本查询条件,并过滤参数
        QueryBuilder query= buildBasicQueryWithFilter(request);
        queryBuilder.withQuery(query);

        // 1.2 结果集过滤
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id", "subTitle", "skus"}, null));

        // 1.3 分页和过滤
        searchWithPageAndSort(queryBuilder, request);

        // 1.4 聚合
        queryBuilder.addAggregation(AggregationBuilders.terms("category").field("cid3"));
        queryBuilder.addAggregation(AggregationBuilders.terms("brand").field("brandId"));


        // 2. 查询，获取结果
        AggregatedPage<Goods> pageInfo = (AggregatedPage<Goods>) goodsRepository.search(queryBuilder.build());


        // 3.解析聚合结果
        // 3.1 商品分类的聚合结果
        List<Map<String,Object>> categories = getCategoryAggResult(pageInfo.getAggregation("category"));

        // 3.2 品牌的聚合结果
        List<Brand> brands = getBrandAggResult(pageInfo.getAggregation("brand"));

        // 3.3 规格参数的聚合,只有一个分类时进行规格参数的聚合
        List<Map<String,Object>> specs=new ArrayList<>();
        if(categories.size() == 1){
            Long cid = (Long) categories.get(0).get("id");
            specs=getSpecs(cid,query);
        }

        // 4.返回结果
        return new SearchResult(pageInfo.getTotalElements(),pageInfo.getTotalPages(),pageInfo.getContent(),categories,brands,specs);
    }

    //构建基本过滤条件
    private QueryBuilder buildBasicQueryWithFilter(SearchRequest request) {
        //添加基础查询条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must();

        //添加过滤条件
        BoolQueryBuilder filterQueryBuilder= QueryBuilders.boolQuery();
        for (Map.Entry<String,String> entry : request.getFilter().entrySet()) {
            String key=entry.getKey();

            if(StringUtils.equals("品牌",key)){
                key="brandId";
            }else if(StringUtils.equals("分类",key)){
                key="cid3";
            }else{
                key="specs."+key+".keyword";
            }

            filterQueryBuilder.must(QueryBuilders.termQuery(key,entry.getValue()));
        }

        boolQueryBuilder.filter(filterQueryBuilder);
        return boolQueryBuilder;
    }

    //获得规格参数的集合数据
    private List<Map<String,Object>> getSpecs(Long cid, QueryBuilder basicQueryBuilder) {
        //获得需要集合的规格参数
        List<SpecParam> params = specificationClient.querySpecParams(null, cid, true, null);

        // 构建自定义搜索构建器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        queryBuilder.withQuery(basicQueryBuilder);

        //遍历结果集,添加规格参数过滤
        params.forEach(param -> {
            String key=param.getName();
            queryBuilder.addAggregation(AggregationBuilders.terms(key).field("specs."+key+".keyword"));
        });

        //执行查询
        Map<String,Aggregation> aggs=elasticsearchTemplate.query(queryBuilder.build(), SearchResponse::getAggregations).asMap();

        List<Map<String, Object>> specs = new ArrayList<>();
        //解析聚合结果
        for(Map.Entry<String,Aggregation> entry :aggs.entrySet()){
            String key = entry.getKey();
            StringTerms value = (StringTerms) entry.getValue();

            Map<String,Object> map=new HashMap<>();
            map.put("k",key);
            map.put("options", value.getBuckets().stream().map(StringTerms.Bucket::getKeyAsString));

            specs.add(map);
        }
        return specs;
    }

    //获得品牌的聚合数据
    private List<Brand> getBrandAggResult(Aggregation aggregation) {
        try {
            LongTerms brand = (LongTerms) aggregation;

            ArrayList<Long> ids = new ArrayList<>();
            for (LongTerms.Bucket bucket : brand.getBuckets()) {
                ids.add(bucket.getKeyAsNumber().longValue());
            }

            List<Brand> brands = brandClient.queryBrandsByIds(ids);
            return brands;
        } catch (Exception e) {
            logger.error("品牌聚合出现异常：", e);
        }
        return null;
    }

    //获得商品分类的聚合数据
    private List<Map<String,Object>> getCategoryAggResult(Aggregation aggregation) {
        try {
            List<Map<String,Object>> categories=new ArrayList<>();

            //获得ids
            ArrayList<Long> cids = new ArrayList<>();
            LongTerms categroyAgg = (LongTerms) aggregation;
            for (LongTerms.Bucket bucket : categroyAgg.getBuckets()) {
                cids.add(bucket.getKeyAsNumber().longValue());
            }

            // 根据id查询分类名称
            List<String> names = categoryClient.queryNamesByIds(cids);

            for (int i = 0; i < cids.size(); i++) {
                HashMap<String, Object> map = new HashMap<>();
                map.put("id",cids.get(0));
                map.put("name",names.get(0));
                categories.add(map);
            }

            return categories;
        } catch (Exception e) {
            logger.error("分类聚合出现异常：", e);
            return null;
        }
    }

    //分页和排序
    private void searchWithPageAndSort(NativeSearchQueryBuilder queryBuilder, SearchRequest request) {
        //分页
        queryBuilder.withPageable(PageRequest.of(request.getPage() - 1, request.getSize()));

        //排序
        if (StringUtils.isNotBlank(request.getSortBy())) {
            queryBuilder.withSort(SortBuilders.fieldSort(request.getSortBy()).order(request.getDescending() ? SortOrder.DESC : SortOrder.ASC));
        }
    }

    //添加数据到索引库
    public void createIndex(Long id) throws IOException {
        Spu spu = goodsClient.querySpuById(id);

        // 构建商品
        Goods goods = importService.buildGoods(spu);
        // 保存数据到索引库
        this.goodsRepository.save(goods);
    }

    //删除索引库中的数据
    public void deleteIndex(Long id) {
        this.goodsRepository.deleteById(id);
    }
}
